﻿#include "demo.h"

// pthread_mutex_t         mlock = PTHREAD_MUTEX_INITIALIZER;
// static pthread_mutex_t *lock_cs;
static long *lock_count;

static int s_server_verify = SSL_VERIFY_NONE;

#define CHK_NULL(x)       \
    if ((x) == NULL) {    \
        printf("null\n"); \
    }
#define CHK_ERR(err, s)   \
    if ((err) == -1) {    \
        printf(" -1 \n"); \
    }
#define CHK_SSL(err)      \
    if ((err) == -1) {    \
        printf(" -1 \n"); \
    }

#define CAFILE "cert/ca/ca.crt"
#define CERTF "cert/server/server.crt"
#define KEYF "cert/server/server.key"
#define KEY_PWD NULL

// 条件变量用于线程间同步
condition_variable cv;

//
// 互斥锁用于保护共享资源
//
mutex mtx;

typedef struct {
    int verbose_mode;
    int verify_depth;
    int always_continue;
} mydata_t;
int mydata_index;

static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
    return preverify_ok;
}

static int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx, char *filename, char *pass) {
    EVP_PKEY *pkey = NULL;
    BIO      *key  = NULL;
    key            = BIO_new(BIO_s_file());
    BIO_read_filename(key, filename);
    pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, pass);
    if (pkey == NULL) {
        printf("PEM_read_bio_PrivateKey err");
        return -1;
    }
    if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) {
        printf("SSL_CTX_use_PrivateKey err\n");
        return -1;
    }
    BIO_free(key);
    return 1;
}

int DemoService::free(SSL &ssl, SOCKET s) {
    SSL_free(&ssl);
    closesocket(s);
    return 0;
}

void *DemoService::thread_main() {
    int                err_code = -1;
    SOCKET             s;
    WORD               wVersionRequested;
    WSADATA            wsaData;
    struct sockaddr_in service;
    int                err;
    size_t             client_len;
    SSL_CTX           *ctx;
    SSL               *ssl;
    X509              *client_cert;
    char              *str;
    char               buf[1024];
    SSL_METHOD        *meth;

    while (TRUE) {
        {
            // 获取锁
            unique_lock<mutex> lock(mtx);
            // 等待条件变量通知
            cv.wait(lock, [this] { return !this->clients.empty(); });
            ssl = this->clients.front();
            this->clients.pop();
        }

        unsigned long iMode = 1;

        s = SSL_get_fd(ssl);

        // set to non-blocking
        /*if (ioctlsocket(s, FIONBIO, (unsigned long *)&iMode) < 0) {
            closesocket(s);
        }*/
        SSL_set_accept_state(ssl);
        // err = SSL_connect(ssl);
        err = SSL_accept(ssl);
        // err = SSL_do_handshake(ssl);
        // while (true) {
        //     err = SSL_do_handshake(ssl);
        //     if (err == SSL_ERROR_SSL) {
        //         // 握手成功
        //         break;
        //     }
        //     int ssl_err = SSL_get_error(ssl, err);
        //     if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) {
        //         // 继续等待
        //         continue;
        //     } else {
        //         // 其他错误，打印日志
        //         ERR_print_errors_fp(stderr);
        //         break;
        //     }
        // }
        printf("----------------------------------------------------------------------\n");
        if (err < 0) {
            printf("err str: %s\n", SSL_state_string_long(ssl));

            printf("ssl get-err: %d\n", SSL_get_error(ssl, err));
            if (SSL_get_error(ssl, err) == SSL_ERROR_WANT_READ) {
                printf("ssl get-err: SSL_ERROR_WANT_READ\n");
            }

            ERR_print_errors_fp(stderr);
            printf("ssl accept-err: %d\n", err);
            free(*ssl, s);
            // return &err_code;
        } else {
            printf("SSL connection using %s\n", SSL_get_cipher(ssl));
            client_cert = SSL_get_peer_certificate(ssl);
            if (client_cert != NULL) {
                printf("Client certificate:\n");
                str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
                CHK_NULL(str);
                printf("\t subject: %s\n", str);
                OPENSSL_free(str);
                str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
                CHK_NULL(str);
                printf("\t issuer: %s\n", str);
                OPENSSL_free(str);
                X509_free(client_cert);
            } else
                printf("Client does not have certificate.\n");

            memset(buf, 0, 1024);
            err = SSL_read(ssl, buf, sizeof(buf) - 1);
            if (err < 0) {
                printf("ssl read err\n");
                free(*ssl, s);
                // return &err_code;
            } else {
                printf("get : %s\n", buf);
#if 1
                buf[err] = '\0';
                err      = SSL_write(ssl, "I hear you.", strlen("I hear you."));
                CHK_SSL(err);
#endif
                free(*ssl, s);
            }
        }
    }
}

// pthread_t pthreads_thread_id(void) {
//     pthread_t ret;
//     ret = pthread_self();
//     return (ret);
// }

void DemoService::pthreads_locking_callback(int mode, int type, char *file,
                                            int line) {

    unique_lock<mutex> lck(mtx);

    if (mode & CRYPTO_LOCK) {
        lck.lock();
        // pthread_mutex_lock(&(lock_cs[type]));
        lock_count[type]++;
    } else {
        lck.unlock();

        // pthread_mutex_unlock(&(lock_cs[type]));
    }
}

int DemoService::test() {
    int                err;
    int                i;
    SOCKET             server_socket, client_socket;
    WORD               wVersionRequested;
    WSADATA            wsaData;
    struct sockaddr_in service;
    size_t             client_len;
    SSL_CTX           *ctx;
    SSL               *ssl;
    X509              *client_cert;
    char              *str;
    char               buf[1024];
    thread::id         pid;
    const SSL_METHOD  *methed;

    // SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CAFILE));
    wVersionRequested = MAKEWORD(2, 2);
    err               = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        printf("err\n");
        return -1;
    }

    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();

    methed = SSLv23_server_method();
    // methed = TLSv1_2_server_method();
    ctx = SSL_CTX_new(methed);
    if (!ctx) {
        ERR_print_errors_fp(stderr);
        exit(2);
    }
    if ((!SSL_CTX_load_verify_locations(ctx, CAFILE, NULL)) ||
        (!SSL_CTX_set_default_verify_paths(ctx))) {
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }
    /*if (SSL_CTX_use_PrivateKey_file_pass(ctx, KEYF, KEY_PWD) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }*/
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }
    if (!SSL_CTX_check_private_key(ctx)) {
        fprintf(stderr, "Private key does not match the certificate public key\n");
        exit(5);
    }
    s_server_verify = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                      SSL_VERIFY_CLIENT_ONCE;
    // SSL_CTX_set_verify(ctx, s_server_verify, verify_callback);
    // SSL_CTX_set_verify(ctx, s_server_verify, NULL);

    // SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);

    server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (server_socket < 0)
        return -1;

    char opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    service.sin_family      = AF_INET;
    service.sin_addr.s_addr = INADDR_ANY;
    // service.sin_addr.s_addr = inet_addr("192.168.6.173");
    service.sin_port = htons(443);
    if (bind(server_socket, (SOCKADDR *)&service, sizeof(service)) == SOCKET_ERROR) {
        printf("bind() failed.\n");
        closesocket(server_socket);
        return -1;
    }
    if (listen(server_socket, 1) == SOCKET_ERROR)
        printf("Error listening on socket.\n");
    printf("recv .....\n");
    /*lock_cs    = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
    lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
    for (i = 0; i < CRYPTO_num_locks(); i++) {
        lock_count[i] = 0;
        pthread_mutex_init(&(lock_cs[i]), NULL);
    }*/
    // CRYPTO_set_id_callback((unsigned long (*)())this_thread::get_id);
    // CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);

    std::vector<std::thread> workers;
    for (int i = 0; i < 4; i++) {
        workers.emplace_back(thread(&DemoService::thread_main, this));
    }
    /*client_socket = accept(s, NULL, NULL);
    ssl           = SSL_new(ctx);
    err           = SSL_set_fd(ssl, client_socket);
    int ret = SSL_accept(ssl);
    printf("ssl ret: %d", ret);*/
    struct sockaddr_in address;
    int                addrlen = sizeof(address);

    // while (1) {
    //     client_socket = accept(server_socket, (SOCKADDR *)&address, &addrlen);
    //     ssl           = SSL_new(ctx);
    //     err           = SSL_set_fd(ssl, client_socket);

    //    if (SSL_accept(ssl) <= 0) {
    //        ERR_print_errors_fp(stderr);
    //    } else {
    //        printf("连接成功");
    //        // 连接成功，可以进行数据读写
    //    }
    //}

    while (true) {
        struct timeval tv;
        fd_set         fdset;
        tv.tv_sec  = 1;
        tv.tv_usec = 0;
        FD_ZERO(&fdset);
        FD_SET(server_socket, &fdset);
        // select(s + 1, &fdset, NULL, NULL, (struct timeval *)&tv);
        if (FD_ISSET(server_socket, &fdset)) {
            client_socket = accept(server_socket, (SOCKADDR *)&address, &addrlen);
            ssl           = SSL_new(ctx);
            CHK_NULL(ssl);
            err = SSL_set_fd(ssl, client_socket);
            if (err > 0) {
                if (client_socket == INVALID_SOCKET) {
                    // Handle error
                    closesocket(client_socket); // Close the client socket when done
                    continue;
                }
                // Successfully accepted a connection
                // Here you can handle the client socket, e.g., read/write data
                {
                    lock_guard<mutex> lock(mtx);
                    clients.push(ssl);
                }
                cv.notify_one();
            } else
                continue;
        }
    }
    free(*ssl, server_socket);
    return 0;
}